﻿using System;
using System.IO;
using System.Web;
using System.Data;
using System.Xml;
using System.Net;
using System.Text;
using System.Threading;
using SyCODE.Component;

namespace SyCODE.Blog.TrackBack
{
    /// <summary>
    /// 异步执行 Ping 命令完成时的事件委托
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void PingCompeletedHandler(object sender, PingEventArgs e);

    /// <summary>
    /// 异步执行 Ping 命令完成时的参数定义
    /// </summary>
    public sealed class PingEventArgs : EventArgs
    {
        private int _error = 0;
        /// <summary>
        /// 获取Ping的结果错误数
        /// </summary>
        public int Error
        {
            get
            {
                return this._error;
            }
        }

        private string _message = String.Empty;
        /// <summary>
        /// 获取Ping 的结果错误信息
        /// </summary>
        public string Message
        {
            get
            {
                return this._message;
            }
        }
        private PingEventArgs(XmlNode error, XmlNode message)
        {
            if (error != null)
            {
                Int32.TryParse(error.InnerText, out this._error);
                if (message != null)
                {
                    this._message = message.InnerText;
                }
            }
            else
            {
                throw new System.Net.WebException("Not Receive", WebExceptionStatus.ReceiveFailure);
            }
        }
        /// <summary>
        /// Ping的结果参数定义的构造方法
        /// </summary>
        /// <param name="response">Ping的结果中的response节点</param>
        internal PingEventArgs(XmlNode response)
            : this(response.SelectSingleNode("Error"), response.SelectSingleNode("Message"))
        {
        }
    }

    /// <summary>
    /// 提供外部TrackBack的Ping的实现
    /// </summary>
    public class Ping
    {
        /// <summary>
        /// 异步执行 Ping 命令完成时的事件
        /// </summary>
        public event PingCompeletedHandler OnPingCompeletedEventHandler;


        private HttpWebRequest request;

        private string target = String.Empty;
        /// <summary>
        /// 获取或设置Ping命令的发送目标
        /// </summary>
        public string Target
        {
            set
            {
                this.target = value;
            }
            get
            {
                return this.target;
            }
        }

        private string title = String.Empty;
        /// <summary>
        /// 获取或设置Trackback的引用文章的标题
        /// </summary>
        public string Title
        {
            set
            {
                this.title = value;
            }
            get
            {
                return this.title;
            }
        }

        private string url = String.Empty;
        /// <summary>
        /// 获取或设置Trackback的引用文章的URL地址
        /// </summary>
        public string Url
        {
            set
            {
                this.url = value;
            }
            get
            {
                return this.url;
            }
        }

        private string excerpt = String.Empty;
        /// <summary>
        /// 获取或设置Trackback的引用文章的摘要
        /// </summary>
        public string Excerpt
        {
            set
            {
                this.excerpt = value;
            }
            get
            {
                return this.excerpt;
            }
        }

        private string blogName = String.Empty;
        /// <summary>
        /// 获取或设置Trackback的引用文章的作者BLOG名称
        /// </summary>
        public string BlogName
        {
            set
            {
                this.blogName = value;
            }
            get
            {
                return this.blogName;
            }
        }

        /// <summary>
        /// 获得Ping的内容的字节数组以备发送
        /// </summary>
        /// <returns></returns>
        public byte[] GetPingData()
        {
            string data = "url=" + HttpUtility.UrlEncodeUnicode(this.url);
            if (this.title.Length > 0)
                data += "&title=" + HttpUtility.UrlEncodeUnicode(this.title);
            if (this.excerpt.Length > 0)
                data += "&excerpt=" + HttpUtility.UrlEncodeUnicode(Function.StringCut(this.excerpt, 255));
            if (this.blogName.Length > 0)
                data += "&blog_name=" + HttpUtility.UrlEncodeUnicode(this.blogName);
            return Encoding.UTF8.GetBytes(data);
        }

        /// <summary>
        /// Ping的功能构造函数
        /// </summary>
        /// <param name="target">发送目标</param>
        /// <param name="url">引用文章的URL地址</param>
        public Ping(string target, string url)
        {
            this.target = target;
            this.url = url;
        }

        /// <summary>
        /// Ping的功能构造函数
        /// </summary>
        /// <param name="target">发送目标</param>
        /// <param name="title">引用文章的标题</param>
        /// <param name="url">引用文章的URL地址</param>
        /// <param name="excerpt">引用文章的摘要</param>
        /// <param name="blogName">引用文章的作者BLOG名称</param>
        public Ping(string target, string title, string url, string excerpt,string blogName)
        {
            this.target = target;
            this.title = title;
            this.url = url;
            this.excerpt = excerpt;
            this.blogName = blogName;
        }

        /// <summary>
        /// 向目标发送该Ping
        /// </summary>
        public void Send()
        {
            //目标URL地址不得为空
            if (this.target.Length == 0)
            {
                throw new WebException("Unknown TrackBack Ping Url", WebExceptionStatus.ConnectFailure);
            }
            //引用文章的URL地址不得为空
            if(this.url.Length == 0)
            {
                throw new WebException("Unknown Source Url", WebExceptionStatus.SendFailure);
            }
            this.request = (HttpWebRequest)WebRequest.Create(this.target);

            this.request.Method = "POST";
            this.request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";

            byte[] data = this.GetPingData();

            this.request.ContentLength = data.Length;

            this.request.AutomaticDecompression = DecompressionMethods.GZip;

            Stream stream = this.request.GetRequestStream();
            stream.Write(data, 0, data.Length);
            stream.Close();

            //开始异步发送Ping
            request.BeginGetResponse(new AsyncCallback( On_PingCompelete ), this);

            //阻止当前线程，直到当前的 WaitHandle 收到信号
            manualResetEvent.WaitOne();
        }

        private static ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        /// <summary>
        /// 异步Ping请求的结果接收方法
        /// </summary>
        /// <param name="ar"></param>
        private static void On_PingCompelete(IAsyncResult ar)
        {
            Ping ping = (Ping)ar.AsyncState;

            //检测并执行事件
            if (ping.OnPingCompeletedEventHandler != null)
            {
                HttpWebResponse response = (HttpWebResponse)ping.request.EndGetResponse(ar);
                XmlDocument res = new XmlDocument();

                try
                {
                    res.Load(response.GetResponseStream());
                    PingEventArgs eventArgs = new PingEventArgs(res.DocumentElement);
                    ping.OnPingCompeletedEventHandler(ping, eventArgs);
                }
                catch
                {
                }
                finally
                {
                    response.Close();
                }
            }

            manualResetEvent.Set();
        }
    }
}
